Skip to content

Conversation

@saoudrizwan
Copy link

@saoudrizwan saoudrizwan commented Dec 25, 2025

Adds two new tools for efficiently managing unread Slack messages 🚀

When you're in a lot of Slack channels (especially with external partner channels), keeping up with unreads becomes overwhelming. The existing conversations_history tool requires you to know which channel to check. These new tools let you:

  1. Get a prioritized view of ALL unread channels in one API call
  2. Filter to only channels where you're @mentioned
  3. Mark channels as read after reviewing them

New Tools

conversations_unreads

Efficiently retrieves unread messages across all channels using a single API call.

Parameters:

Parameter Type Default Description
include_messages bool true Return actual messages or just channel summary
channel_types string "all" Filter: "dm", "group_dm", "partner", "internal", or "all"
max_channels int 50 Limit number of channels returned
max_messages_per_channel int 10 Messages to fetch per channel
mentions_only bool false Only return channels where you have @mentions

Output (CSV):

ChannelID,ChannelName,ChannelType,UnreadCount,LastRead,Latest
D09EDMKJC64,@john,dm,5,,1760563532.527859
C08KX4PV3S6,#ext-partner-company,partner,2,1763493994.468259,1766596058.035729
C09J740AZTR,#engineering,internal,1,1759517692.989219,1762753562.005619

conversations_mark

Marks a channel or DM as read.

Parameters:

Parameter Type Required Description
channel_id string Yes Channel ID, #channel-name, or @username
ts string No Timestamp to mark read up to. If omitted, marks all as read

Technical Implementation

Why client.counts API?

We initially tried using ClientUserBoot but discovered it only returns channels in the user's sidebar, not all channels with unreads. The client.counts API (already implemented in the edge client) is what Slack's web client uses:

  • Returns HasUnreads boolean and MentionCount for ALL channels in one call
  • Separates channels into three arrays: Channels, MPIMs (group DMs), IMs (direct messages)
  • Much more efficient than checking each channel individually

Channel Categorization & Priority

Results are automatically sorted by priority:

  1. DMs (dm) - Direct messages, highest priority
  2. Group DMs (group_dm) - Multi-person direct messages
  3. Partner channels (partner) - Externally shared channels (uses IsExtShared metadata)
  4. Internal channels (internal) - Everything else

Code Changes

pkg/provider/api.go

  • Added IsExtShared field to Channel struct
  • Exposed ClientCounts method on SlackAPI interface
  • Updated mapChannel to pass through IsExtShared

pkg/handler/conversations.go

  • Added UnreadChannel struct for CSV output
  • Added ConversationsUnreadsHandler with priority sorting and filtering
  • Added ConversationsMarkHandler with channel name resolution
  • Added sortChannelsByPriority helper

pkg/server/server.go

  • Registered both new tools with proper annotations

Example Use Cases

1. Morning Inbox Review

"What are my unread messages?"
→ conversations_unreads (returns prioritized list: DMs first, then partner channels, then internal)

2. Priority Inbox - Just @Mentions

"Show me only channels where I'm @mentioned"
→ conversations_unreads with mentions_only: true

3. Check Partner Channels Only

"What's new in external partner channels?"
→ conversations_unreads with channel_types: "partner"

4. Quick Summary Without Messages

"Which channels have unreads?"
→ conversations_unreads with include_messages: false

5. Mark Channel as Read

"Mark #random as read"
→ conversations_mark with channel_id: "#random"

6. Triage Workflow

1. Get unreads summary: conversations_unreads with include_messages: false
2. Review a specific channel: conversations_history
3. Mark it as read: conversations_mark
4. Repeat

Breaking Changes

Users with existing channel caches will need to delete ~/Library/Caches/slack-mcp-server/channels_cache_v2.json (or equivalent) to repopulate with the new IsExtShared field. Otherwise, partner channel detection won't work until the cache is refreshed.

Test Plan

  • conversations_unreads returns prioritized list of unread channels
  • channel_types filter correctly filters by dm/group_dm/partner/internal
  • mentions_only: true filters to only channels with @mentions
  • include_messages: false returns summary without fetching message content
  • Partner channels detected via IsExtShared metadata (not name prefix)
  • conversations_mark marks channels as read using channel ID
  • conversations_mark resolves #channel-name to channel ID
  • conversations_mark resolves @username to DM channel ID
  • conversations_mark without timestamp marks all messages as read

…rieval

- Uses ClientUserBoot to get all channels with LastRead/Latest in one API call
- Filters channels where Latest > LastRead to find unreads
- Prioritizes: DMs > group DMs > partner channels (ext-*) > internal
- Only fetches message history for channels with actual unreads
- Supports filtering by channel type and configurable limits

Addresses issue korotovsky#114
- Switch from ClientUserBoot to ClientCounts API
- ClientCounts returns HasUnreads boolean for all channels
- Add ClientCounts to SlackAPI interface
- Process Channels, MPIMs, and IMs separately
- Strip existing # prefix before adding to avoid ##name
- Use stripped name for ext-/shared- prefix checks for partner type
- Add mentions_only parameter to conversations_unreads to filter
  channels to only those with @mentions (priority inbox)
- Add conversations_mark tool to mark channels/DMs as read
  - Supports channel IDs, #channel names, and @username
  - If no timestamp provided, marks all messages as read
…nels

- Add IsExtShared field to Channel struct in cache
- Pass IsExtShared through mapChannel function
- Use cached.IsExtShared to identify external/partner channels
  instead of checking for ext-/shared- name prefixes

Note: Users may need to delete their channels cache file to repopulate
with the new IsExtShared field.
Copy link
Owner

@korotovsky korotovsky left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @saoudrizwan, thank you for the great contribution!

Since the new API method has been introduced and this MCP supports multiple slack token types, a question from my side: have you checked that ClientCounts also returns data if we provide xoxb or xoxp (as I assume your main setup is xoxc/xoxd pair since you are using undocumented APIs, this is is where this API works the best and has the most coverage)

@korotovsky
Copy link
Owner

Hi @saoudrizwan, would you like to finalize the PR or we must to find someone within the community to pick it up?

- Extract handler params to structs with parsing functions
  (unreadsParams, markParams) following existing patterns
- Add SLACK_MCP_MARK_TOOL env var guard for conversations_mark
  (disabled by default, requires explicit opt-in)
- Remove unused categorizeChannel and getChannelDisplayName functions
- Update README with conversations_mark safety note and env var docs
@saoudrizwan
Copy link
Author

Hey @korotovsky, thanks for the review! I've addressed all the inline comments in 4ffa603:

  • Extracted params to structs with parsing functions (unreadsParams, markParams)
  • Added SLACK_MCP_MARK_TOOL env var guard (disabled by default)
  • Removed the unused categorizeChannel and getChannelDisplayName functions
  • Updated README with the new env var docs

Regarding token compatibility: you're right that I've been testing with xoxc/xoxd tokens. The client.counts API is an undocumented endpoint that the Slack web client uses, so it's designed for session tokens. I haven't tested with xoxb or xoxp tokens.

Looking at the edge client implementation, ClientCounts just passes whatever token is configured. For xoxb/xoxp tokens, I'd expect one of these outcomes:

  1. Works normally (unlikely since it's a web client API)
  2. Returns an error like "not_allowed_token_type"
  3. Returns empty/limited data

If you'd like, I can add a note in the README that conversations_unreads works best with xoxc/xoxd tokens, or we could add a token type check that falls back to a different approach for bot/OAuth tokens. Let me know what you'd prefer!

@korotovsky
Copy link
Owner

Hi @saoudrizwan, thanks for fixes. As of regarding token types, would be ideal if you could manually test xoxp and xoxb and in case one or both of them do not work, then a) make a notice in the README about it b) register the tool only if suitable token is used by MCP, otherwise simply not register the tool, because it would be non-functional anyway.

urisland added a commit to urisland/slack-mcp-server that referenced this pull request Jan 20, 2026
…mark tools

Adds:
- conversations_unreads: Get unread messages across all channels efficiently
  - Priority sorting: DMs > partner channels > internal
  - Supports @mention filtering, channel type filtering
- conversations_mark: Mark channels as read (disabled by default for safety)
- IsExtShared field for partner channel detection
- ClientCounts edge API method

Note: Binary file (build/slack-mcp-server) excluded from merge for security.

Original PR: korotovsky#146
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants